//
//  Runloop.h
//  面试题
//
//  Created by Toj on 8/13/21.
//

// 源码
源码: https://opensource.apple.com/source/CF/CF-1153.18/CFRunLoop.c.auto.html

https://www.jianshu.com/p/37baf61a2ede?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
文章: http://events.jianshu.io/p/13b393ea009f
文章: https://juejin.cn/post/6844903588712415239

sourse: https://blog.csdn.net/shengpeng3344/article/details/104518051

卡顿检测: https://www.jianshu.com/p/d0aab0eb8ce4

#pragma mark - 什么是Runloop
RunLoop 实际上就是一个对象，这个对象管理了其需要处理的事件和消息，并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后，就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中，直到这个循环结束（比如传入 quit 的消息），函数返回

#pragma mark - Runloop 对象结构
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* locked for accessing mode list */
    __CFPort _wakeUpPort;   // used for CFRunLoopWakeUp 内核向该端口发送消息可以唤醒runloop
    Boolean _unused;
    volatile _per_run_data *_perRunData; // reset for runs of the run loop
    pthread_t _pthread;             //RunLoop对应的线程
    uint32_t _winthread;
    CFMutableSetRef _commonModes;    //存储的是字符串，记录所有标记为common的mode
    CFMutableSetRef _commonModeItems;//存储所有commonMode的item(source、timer、observer)
    CFRunLoopModeRef _currentMode;   //当前运行的mode
    CFMutableSetRef _modes;          //存储的是CFRunLoopModeRef
    struct _block_item *_blocks_head;//doblocks的时候用到
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

#pragma mark - RunloopMode 对象结构

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;   //mode名称
    Boolean _stopped;    //mode是否被终止
    char _padding[3];
    //几种事件
    CFMutableSetRef _sources0;  //sources0
    CFMutableSetRef _sources1;  //sources1
    CFMutableArrayRef _observers; //通知
    CFMutableArrayRef _timers;    //定时器
    CFMutableDictionaryRef _portToV1SourceMap; //字典  key是mach_port_t，value是CFRunLoopSourceRef
    __CFPortSet _portSet; //保存所有需要监听的port，比如_wakeUpPort，_timerPort都保存在这个数组中
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

#pragma mark - RunloopMode Mode, 切记 common不是真实的Mode

UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,
启动完成后切换到NSDefaultRunLoopMode就不再使用.
NSDefaultRunLoopMode: App的默认 Mode, 通常主线程是在这个 Mode 下运行的.
NSConnectionReplyMode: NSConnection对象检测回应, 通常用不到.
NSModalPanelRunLoopMode: 模态页面事件.
GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode, 通常用不到.
NSEventTrackingRunLoopMode: 界面跟踪 Mode, 用于 ScrollView 追踪触摸滑动, 保证界面滑动时不受其他 Mode 影响.
NSRunLoopCommonModes: 这是一个占位的 Mode，没有实际作用。
作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用，并不是一种真正的Mode

其他Mode: https://iphonedev.wiki/index.php/CFRunLoop
NSTaskDeathCheckMode
kCFHostBlockingMode
kCFNetServiceMonitorBlockingMode
kCFNetServiceBrowserBlockingMode
kCFNetServiceBlockingMode
kCFStreamSocketReadPrivateMode
kCFStreamSocketCanReadPrivateMode
kCFStreamSocketWritePrivateMode
kCFStreamSocketCanWritePrivateMode
kCFStreamSocketSecurityClosePrivateMode
kCFStreamSocketBogusPrivateMode
kCFURLConnectionPrivateRunLoopMode
kProxySupportLoadingPacPrivateMode
kProxySupportSyncPACExecutionRunLoopMode
kCFStreamSocketSecurityClosePrivateMode
com.apple.securityd.runloop
FigPlayerBlockingRunLoopMode


#pragma mark - RunloopMode item

一个 RunLoop 包含若干个 Mode,
每个 Mode 又包含若干个 Source/Timer/Observer.
每次调用 RunLoop 的主函数时, 只能指定其中一个 Mode, 这个Mode被称作 CurrentMode.
如果需要切换 Mode, 只能退出 Loop, 再重新指定一个 Mode 进入.
这样做主要是为了分隔开不同组的 Source/Timer/Observer, 让其互不影响.

#pragma mark RunloopMode CFRunLoopSource 对象结构
struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits; //用于标记Signaled状态，source0只有在被标记为Signaled状态，才会被处理
    pthread_mutex_t _lock;
    CFIndex _order;         /* immutable */
    CFMutableBagRef _runLoops;
    union {
        CFRunLoopSourceContext version0;     /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;    /* immutable, except invalidation */
    } _context;
};

#pragma mark RunloopMode Source Source0 对象结构
source0是App内部事件，由App自己管理的UIEvent、CFSocket都是source0。
当一个source0事件准备执行的时候，必须要先把它标记为signal状态，以下是source0的结构体：
typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    void    (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*perform)(void *info);
} CFRunLoopSourceContext;
source0是非基于Port的.
只包含了一个回调(函数指针), 它并不能主动触发事件.
使用时, 你需要先调用 CFRunLoopSourceSignal(source), 将这个 Source 标记为待处理, 然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop, 让其处理这个事件.

#pragma mark RunloopMode Source Source1 对象结构
source1由RunLoop和内核管理, source1带有mach_port_t, 可以接收内核消息并触发回调, 以下是source1的结构体
typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
    mach_port_t (*getPort)(void *info);
    void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
    void *  (*getPort)(void *info);
    void    (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;
Source1 除了包含回调指针外 还包含一个mach port.
Source1可以监听系统端口和通过内核和其他线程通信,
接收、分发系统事件, 它能够主动唤醒RunLoop(由操作系统内核进行管理, 例如CFMessagePort消息).
官方也指出可以自定义Source, 因此对于CFRunLoopSourceRef来说它更像一种协议, 框架已经默认定义了两种实现, 如果有必要开发人员也可以自定义,详细情况可以查看官方文档.

#pragma mark RunloopMode CFRunLoopObserver 对象结构
CFRunLoopObserver是观察者, 可以观察RunLoop的各种状态, 并抛出回调
struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;      /* immutable */
    CFIndex _order;         /* immutable */
    CFRunLoopObserverCallBack _callout; /* immutable */
    CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};

当 RunLoop 的状态发生变化时, 观察者就能通过回调接收到这个变化. 可以观测的时间点有以下几个:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
};

#pragma mark RunloopMode CFRunLoopTimer 对象结构
CFRunLoopTimer是基于时间的触发器, 它和 NSTimer 是toll-free bridged(对象桥接)的, 可以相互转换.
其包含一个时间长度和一个回调(函数指针).
当其加入到 RunLoop 时, RunLoop会注册对应的时间点, 当时间点到时, RunLoop会被唤醒以执行那个回调.

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;  //标记fire状态
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop; // 添加该timer的runloop
    CFMutableSetRef _rlModes; // 所有 包含该timer的 mode的 modeName, 意味着一个timer可能会在多个mode中存在.
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;     // 理想时间间隔  /* immutable */
    CFTimeInterval _tolerance;    // 时间偏差      /* mutable */
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
};


#pragma mark RunloopMode 总结
上面的 Source/Timer/Observer 被统称为 mode item.
一个 item 可以被同时加入多个 mode.
但一个 item 被重复加入同一个 mode 时是不会有效果的.
如果一个mode 中一个 item 都没有, 则 RunLoop 会直接退出, 不进入循环.


#pragma mark - Runloop的创建

CFRunLoopRef CFRunLoopGetMain() {
    return _CFRunLoopGet(pthread_main_thread_np());
}

CFRunLoopRef CFRunLoopGetCurrent() {
    return _CFRunLoopGet(pthread_self());
}

/// 全局的Dictionary，key 是 pthread_t， value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;
 
/// 获取一个 pthread 对应的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
    OSSpinLockLock(&loopsLock);
    
    if (!loopsDic) {
        // 第一次进入时，初始化全局Dic，并先为主线程创建一个 RunLoop。
        loopsDic = CFDictionaryCreateMutable();
        // 创建runloop
        CFRunLoopRef mainLoop = _CFRunLoopCreate();
        // 跟主线程绑定
        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
    }
    
    /// 直接从 Dictionary 里获取。
    CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
    
    if (!loop) {
        /// 取不到时，创建一个
        loop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, thread, loop);
        /// 注册一个回调，当线程销毁时，为 Run Loop 结束时进行清理和善后工作
        _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
    }
    
    OSSpinLockUnLock(&loopsLock);
    return loop;
}


#pragma mark - RunLoop的实现 do { CFRunLoopRunSpecific() } while()
/**
 * RunLoop的实现
 * Parameter: runloop: 执行的Runloop
 * Parameter: modeName: 运行模式
 * Parameter: seconds: 超时时间
 * Parameter: stopAfterHandle:
 */
int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
    
    /// 首先根据modeName找到对应mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
    /// 如果mode里没有source/timer/observer, 直接返回。
    if (__CFRunLoopModeIsEmpty(currentMode)) return;
    
    /// 1. 通知 Observers: RunLoop 即将进入 loop。
    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
    
    /// 内部函数，进入loop
    __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
        
        Boolean sourceHandledThisLoop = NO;
        int retVal = 0;
        do {
 
            /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
            /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
            /// 执行被加入的block
            __CFRunLoopDoBlocks(runloop, currentMode);
            
            /// 4. RunLoop 触发 Source0 (非port) 回调。
            sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
            /// 执行被加入的block
            __CFRunLoopDoBlocks(runloop, currentMode);
 
            /// 5. 如果有 Source1 (基于port) 处于 ready 状态，直接处理这个 Source1 然后跳转去处理消息。
            if (__Source0DidDispatchPortLastTime) {
                Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
                if (hasMsg) goto handle_msg;
            }
            
            /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。
            if (!sourceHandledThisLoop) {
                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
            }
            
            /// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。
            /// • 一个基于 port 的Source 的事件。
            /// • 一个 Timer 到时间了
            /// • RunLoop 自身的超时时间到了
            /// • 被其他什么调用者手动唤醒
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
                mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
            }
 
            /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
            
            /// 收到消息，处理消息。
            handle_msg:
 
            /// 9.1 如果一个 Timer 到时间了，触发这个Timer的回调。
            if (msg_is_timer) {
                __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
            }
 
            /// 9.2 如果有dispatch到main_queue的block，执行block。
            else if (msg_is_dispatch) {
                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            }
 
            /// 9.3 如果一个 Source1 (基于port) 发出事件了，处理这个事件
            else {
                CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
                sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
                if (sourceHandledThisLoop) {
                    mach_msg(reply, MACH_SEND_MSG, reply);
                }
            }
            
            /// 执行加入到Loop的block
            __CFRunLoopDoBlocks(runloop, currentMode);
            
 
            if (sourceHandledThisLoop && stopAfterHandle) {
                /// 进入loop时参数说处理完事件就返回。
                retVal = kCFRunLoopRunHandledSource;
            } else if (timeout) {
                /// 超出传入参数标记的超时时间了
                retVal = kCFRunLoopRunTimedOut;
            } else if (__CFRunLoopIsStopped(runloop)) {
                /// 被外部调用者强制停止了
                retVal = kCFRunLoopRunStopped;
            } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
                /// source/timer/observer一个都没有了
                retVal = kCFRunLoopRunFinished;
            }
            
            /// 如果没超时，mode里没空，loop也没被停止，那继续loop。
        } while (retVal == 0);
    }
    
    /// 10. 通知 Observers: RunLoop 即将退出。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}
